package com.pan.download5.ctrl;

import com.pan.entity.Chapter;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * 小说章节爬取任务发放
 * 小说章节爬取任务方法需要保证
 * 1.每个任务同时只有一个线程执行
 * 2.需要保证发放的任务是当前任务列表里执行序列里最小的
 * 3.需要保证当前有线程执行当前未完成的任务序列里最小的
 */
public class LinkedTaskPublish implements TaskSyncPublish {

    //任务链表
    private LinkedList<Chapter> taskList;
    //要操作taskList必须要拿到许可才能操作
    //这里用CAS来保证taskList的安全即可
//    private AtomicInteger permit = new AtomicInteger(1);
    //听别人劝说，不要自己写并发，多使用 JDK 并发包提供的并发容器和工
    //具类来解决并发问题，因为这些类都已经通过了充分的测试和优化
    private Semaphore semaphore = new Semaphore(1);//貌似这个锁太重量级了

    public LinkedTaskPublish(List<Chapter> chapterList) {
        taskList = new LinkedList<>(chapterList);//这里面是调用addAll方法
        //看过源码，addAll是 将Collection.toArrays 再来 add是能保证顺序的
    }

    //这里cas获取许可能保证同时只有一个线程操作taskList,
    // 就算只有一个线程其一旦不能完成任务，归还任务，该任务放在最前，
    // 该线程空闲下来后再次获取任务仍然是最小任务
    //领取任务
    public synchronized Chapter publish() {
//        用现成的concurrent工具不要自己写
//        while (!permit.compareAndSet(1, 0)) ;
        try {
            semaphore.acquire();
            if (taskList.size() > 0) {
                return taskList.removeFirst();
            }
            return null;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            semaphore.release();
//            while (!permit.compareAndSet(0, 1)) ;
        }
    }

    //任务取消
    //可能不搞这个会好点
    //但需要确保线程拿到任务必定能执行成功，还是搞一下比较好
    public synchronized void cancel(Chapter chapter) {
//        用现成的jdk的concurrent工具不要自己写
//        while (!permit.compareAndSet(1, 0)) ;

        try {
            semaphore.acquire();
            for (int i = 0; i < taskList.size(); i++) {
                Chapter c = taskList.get(i);
                if (chapter.getId()<c.getId()){
                    taskList.add(i,chapter);
                    return;
                }
            }
            taskList.addLast(chapter);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            semaphore.release();
//            while (!permit.compareAndSet(0,1));
        }
    }

}
